import { Button, CCFloat, Canvas, Color, Component, EditBox, EventMouse, EventTouch, Graphics, Label, Node, NodeEventType, Prefab, Sprite, Toggle, UITransform, Vec2, Vec3, _decorator, director, misc, tween, v2 } from 'cc';
import NodeFactory from './NodeFactory';
import { Point } from './Point';
const { ccclass, property } = _decorator;

@ccclass('Paper')
export class Paper extends Component {

    @property(Toggle)
    forwardRight: Toggle = null;

    @property(EditBox)
    speedEdit: EditBox = null;

    @property(Button)
    resetScaleBtn: Button = null;

    @property(Toggle)
    touchMoveScale: Toggle = null;

    @property(Node)
    targetNode: Node = null;

    @property(Prefab)
    pointPre: Prefab = null;

    @property(Color)
    lineColor: Color = null;

    @property(CCFloat)
    lineWidth: number = 5;

    @property(CCFloat)
    sampleNum: number = 1000;

    @property(Node)
    lineContainer: Node = null;

    @property(Node)
    ctrlContainer: Node = null;

    @property(Node)
    graphicsContainer: Node = null;

    @property(Label)
    paperScale: Label = null;

    @property(Button)
    addCtrlPoint: Button = null;

    @property(Button)
    removeCtrlPoint: Button = null;

    @property(Button)
    standardBtn: Button = null;

    @property(Button)
    savePointBtn: Button = null;

    @property(Button)
    helpBtn: Button = null;

    @property(Button)
    runSavePointBtn: Button = null;

    @property(Node)
    helpContent: Node = null;

    private canvasNode: Node = null;
    private pointPool: NodeFactory = null;
    /**所有控制点坐标 */
    private ctrlPoints: Vec2[] = [];

    /**当前选中的控制点 */
    public curCtrlPoint: Point = null;
    private curLineWidth: number = 1;

    private totalPoints: Vec2[] = [];
    private interval: number = 10;//间距

    private speed: number = 0;

    onLoad() {
        this.helpBtn.node.active = false;
        this.canvasNode = director.getScene().getComponentInChildren(Canvas).node;
        this.addEvents();
        this.curLineWidth = this.lineWidth;
        this.pointPool = new NodeFactory(this.pointPre);
        this.pointPool.init(this.sampleNum);
        this.speed = Number(this.speedEdit.string);
        this.speedEdit.node.on(EditBox.EventType.TEXT_CHANGED, this.onSpeedChange, this);
        this.randomDraw(3);
    }

    onSpeedChange() {
        this.speed = Number(this.speedEdit.string);
    }

    onDestroy() {
        this.removeEvents();
    }

    private addEvents() {
        this.canvasNode.on(NodeEventType.TOUCH_MOVE, this.onTouchMove, this);
        this.canvasNode.on(NodeEventType.MOUSE_WHEEL, this.onMouseWheel, this);
        this.addCtrlPoint.node.on(Button.EventType.CLICK, this.onAddCtrl, this);
        this.removeCtrlPoint.node.on(Button.EventType.CLICK, this.onRemoveCtrl, this);
        this.standardBtn.node.on(Button.EventType.CLICK, this.onStandard, this);
        this.savePointBtn.node.on(Button.EventType.CLICK, this.onSaveCtrl, this);
        this.helpBtn.node.on(Button.EventType.CLICK, this.onClickHelp, this);
        this.runSavePointBtn.node.on(Button.EventType.CLICK, this.onRunSaveCtrl, this)
        this.resetScaleBtn.node.on(Button.EventType.CLICK, this.onResetScale, this)
    }

    private removeEvents() {
        this.canvasNode.off(NodeEventType.TOUCH_MOVE, this.onTouchMove, this);
        this.canvasNode.off(NodeEventType.MOUSE_WHEEL, this.onMouseWheel, this);
        this.addCtrlPoint.node.off(Button.EventType.CLICK, this.onAddCtrl, this);
        this.removeCtrlPoint.node.off(Button.EventType.CLICK, this.onRemoveCtrl, this);
        this.standardBtn.node.off(Button.EventType.CLICK, this.onStandard, this);
        this.savePointBtn.node.off(Button.EventType.CLICK, this.onSaveCtrl, this);
        this.helpBtn.node.off(Button.EventType.CLICK, this.onClickHelp, this);
        this.runSavePointBtn.node.off(Button.EventType.CLICK, this.onRunSaveCtrl, this)
        this.resetScaleBtn.node.on(Button.EventType.CLICK, this.onResetScale, this)

    }

    private onRunSaveCtrl() {
        if (this.totalPoints.length == 0) {
            console.log("请先添加点");
            return;
        }

        let sequenceEasing = [];
        // 初始位置立即定位到第一个点
        const firstPoint = this.totalPoints[0];
        sequenceEasing.push(
            tween(this.targetNode)
                .set({ position: new Vec3(firstPoint.x, firstPoint.y, 0) })
                .call(() => {
                    this.targetNode.angle = misc.radiansToDegrees(firstPoint.angle) + (this.forwardRight.isChecked ? 180 : 0);
                })
        );

        // 遍历所有点计算分段移动时间
        for (let i = 0; i < this.totalPoints.length - 1; i++) {
            const pCurrent = this.totalPoints[i];
            const pNext = this.totalPoints[i + 1];
            const distance = Vec2.distance(pCurrent, pNext);
            const time = distance / this.speed;

            sequenceEasing.push(
                tween(this.targetNode)
                    .call(() => {
                        this.targetNode.angle = misc.radiansToDegrees(pNext.angle) + (this.forwardRight.isChecked ? 180 : 0);
                    })
                    .to(time, { position: new Vec3(pNext.x, pNext.y, 0) }, { easing: "linear" })
            );
        }
        tween(this.targetNode).sequence(...sequenceEasing).start();
    }


    private onResetScale() {
        this.node.setScale(1, 1);
        this.paperScale.string = '1';
        this.curLineWidth = this.lineWidth * (1 / this.node.scale.x);
        this.draw();
    }




    private onSaveCtrl(e: EventTouch) {
        this.totalPoints.length = 0;

        let points = this.ctrlPoints;
        //计算路径坐标
        let n = points.length - 1;
        for (let i = 0, len = this.sampleNum; i < len; i++) {
            let t = i / len, x = 0, y = 0;
            let item: number, ctrl: Vec2;
            for (let j = 0; j < n + 1; j++) {
                item = this.comb(n, j) * Math.pow(1 - t, n - j) * Math.pow(t, j);
                ctrl = points[j];
                x += ctrl.x * item;
                y += ctrl.y * item;
            }
            if (i % this.interval === 0 || i === this.sampleNum - 1) {
                // 计算导数（切线方向）
                let dx = 0, dy = 0;
                const n = points.length - 1;
                if (n > 0) { // 至少需要2个控制点才能计算切线
                    for (let j = 0; j < n; j++) {
                        const derivativeCoeff = n * this.comb(n - 1, j) * Math.pow(1 - t, n - 1 - j) * Math.pow(t, j);
                        dx += (points[j + 1].x - points[j].x) * derivativeCoeff;
                        dy += (points[j + 1].y - points[j].y) * derivativeCoeff;
                    }
                }

                // 计算角度（弧度）并存储为Vec2(x, y, angle)
                const angle = Math.atan2(dy, dx);
                const pointWithData = v2(x, y);
                (pointWithData as any).angle = angle; // 扩展Vec2类型存储角度
                this.totalPoints.push(pointWithData);
            }
        }


        let dataArr = [];
        for (let i = 0; i < this.totalPoints.length; i++) {
            const p = this.totalPoints[i];
            let arr = [];
            arr.push(p.x, p.y, misc.radiansToDegrees(p.angle))
            dataArr.push(arr)
        }



        console.log('采样点数据（坐标及角度）:', dataArr);
    }





    private onTouchMove(e: EventTouch) {
        if (!this.touchMoveScale.isChecked) return
        let delta = e.touch.getDelta();
        this.node.position = this.node.position.add3f(delta.x, delta.y, 0);
    }

    private onMouseWheel(e: EventMouse) {
        if (!this.touchMoveScale.isChecked) return
        // console.log('滚动：', e, e.getScrollX(), e.getScrollY());
        let long = e.getScrollY();
        //向上滚 放大
        let v_old = this.node.scale.x;
        let v = long > 0 ? v_old * 1.1 : v_old * 0.9;
        this.paperScale.string = v.toString();
        this.node.setScale(v, v);
        this.curLineWidth = this.lineWidth * (1 / this.node.scale.x);
        this.draw();
    }

    private randomDraw(stage: number = 3) {
        // for (let i = 0; i < stage + 1; i++) {
        //     let x = Math.round(this.random(-1136 / 2, 1136 / 2));
        //     let y = Math.round(this.random(-640 / 2, 640 / 2));
        //     this.ctrlPoints.push(v2(x, y));
        // }
        let p0 = v2(-422, 78);
        let p1 = v2(75, -87);
        let p2 = v2(-160, 157);
        let p3 = v2(380, -1);
        this.ctrlPoints.push(p0, p1, p2, p3);
        this.draw();
    }

    private random(min: number, max: number): number {
        return Math.random() * (max - min) + min;
    }

    /**控制点更新后 重绘曲线 */
    public updateCtrls() {
        this.ctrlPoints.length = this.ctrlContainer.children.length;
        for (let i = 0; i < this.ctrlPoints.length; i++) {
            let child = this.ctrlContainer.children[i];
            let point = this.ctrlPoints[i];
            if (!point) point = v2();
            point.x = child.position.x;
            point.y = child.position.y;
            this.ctrlPoints[i] = point;
        }
        this.draw();
    }

    public draw() {
        this.drawCtrls();
        let points = this.ctrlPoints;
        //计算路径坐标
        let n = points.length - 1;
        for (let i = 0, len = this.sampleNum; i < len; i++) {
            let t = i / len, x = 0, y = 0;
            let item: number, ctrl: Vec2;
            for (let j = 0; j < n + 1; j++) {
                item = this.comb(n, j) * Math.pow(1 - t, n - j) * Math.pow(t, j);
                ctrl = points[j];
                x += ctrl.x * item;
                y += ctrl.y * item;
            }
            // this.line1(x, y, i);
            this.drawLines(x, y, i);
        }
    }


    // /**方式1： 点累积成线 */
    // private line1(x: number, y: number, i: number) {
    //     this.lineContainer.children.forEach(child => { this.pointPool.putNode(child) });
    //     let node = this.lineContainer.children[i];
    //     if (!node) {
    //         node = this.pointPool.getNode();
    //         node.parent = this.lineContainer;
    //     }
    //     node.getComponent(Sprite).color = this.lineColor;
    //     let ui = node.getComponent(UITransform);
    //     ui.width = ui.height = this.lineWidth;
    //     node.setPosition(v3(x, y));
    // }

    /**绘制控制点*/
    private drawCtrls() {
        let children = this.ctrlContainer.children;
        let points = this.ctrlPoints;
        //如果有多余的点 先回收
        while (children.length > points.length) {
            let last = children[children.length - 1];
            this.pointPool.putNode(last);
        }
        for (let i = 0; i < points.length; i++) {
            let child = children[i];
            if (!child) {
                child = this.pointPool.getNode();
                child.parent = this.ctrlContainer;
                child.setSiblingIndex(i);
                let ui = child.getComponent(UITransform);
                ui.width = ui.height = this.curLineWidth * 5;
                child.getComponent(Sprite).color = Color.GREEN;
                child.getComponent(Point).init(this);
            }
            let x = points[i].x, y = points[i].y;
            child.getComponent(Point).reset(i, x, y);
        }
    }

    /**方式2： canvas2d绘制 */
    private drawLines(x: number, y: number, i: number) {
        let g = this.graphicsContainer.getComponent(Graphics);
        g.lineWidth = this.curLineWidth;
        if (i === 0) {
            g.clear();
            g.moveTo(x, y);
        } else g.lineTo(x, y);
        if (i === this.sampleNum - 1) g.stroke();
    }

    // private ctrls2() {
    //     let g = this.graphicsContainer.getComponent(Graphics);
    //     let ctrls = this.ctrlPoints;
    //     for (let k = 0; k < ctrls.length; k++) {
    //         g.circle(ctrls[k].x, ctrls[k].y, this.lineWidth * 1.5);
    //         g.fillColor = Color.YELLOW;
    //         g.fill();

    //     }
    // }

    /**计算阶乘 */
    private stageMultiList: number[] = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800];
    private stageMulti(num: number): number {
        let res = this.stageMultiList[num];
        if (!res) {
            res = num * this.stageMulti(num - 1);
            this.stageMultiList[num] = res;
        }
        return res;
    }

    /** 计算组合数 */
    private comb(n: number, i: number): number {
        return this.stageMulti(n) / this.stageMulti(i) / this.stageMulti(n - i);
    }


    /**增加控制点 */
    private onAddCtrl() {
        if (!this.curCtrlPoint) return;
        let idx = this.curCtrlPoint.index;
        let temp = this.ctrlPoints.splice(idx + 1);
        this.ctrlPoints.push(v2());
        this.ctrlPoints.push(...temp);
        this.draw();
    }

    /**减少控制点 */
    private onRemoveCtrl() {
        if (!this.curCtrlPoint) return;
        if (this.ctrlPoints.length < 2) return;
        let idx = this.curCtrlPoint.index;
        this.ctrlPoints.splice(idx, 1);
        this.draw();
    }

    /**标准化曲线 起点设置在（0, 0） */
    private onStandard() {
        // let first: Vec2 = this.ctrlPoints[0].clone();
        // if (!first) return;
        // for (let item of this.ctrlPoints) {
        //     item.subtract(first);
        // }
        // this.draw();
        this.ctrlPoints.length = 0;
        let p0 = v2(-422, 78);
        let p1 = v2(75, -87);
        let p2 = v2(-160, 157);
        let p3 = v2(380, -1);
        this.ctrlPoints.push(p0, p1, p2, p3);
        this.draw();
    }

    private onClickHelp() {
        this.helpContent.active = !this.helpContent.active;
    }
}

